package game;

import java.util.Random;
import java.util.Set;

import userInterface.CollisionListener;

import exception.*;

public class Facade implements IFacade<World,Ship,Asteroid,Bullet> {
	
	/**
	 * Create a new Facade object.
	 */
	public Facade() {
		
	}
	
	/**
	 * Create a new world with the given <code>width</code> and
	 * <code>height</code>.
	 */
	@Override
	public World createWorld(double width, double height) {
		try {
		  return new World(width,height);
		}
		catch(Exception exc) {
			throw new ModelException("");
		}
	}

	/**
	 * Return the width of <code>world</code>.
	 */
	@Override
	public double getWorldWidth(World world) {
		  return world.getWidth();
	  }

	/**
	 * Return the height of <code>world</code>.
	 */
	@Override
	public double getWorldHeight(World world) {
		  return world.getHeight();
	  }

	/**
	 * Return all ships located within <code>world</code>.
	 */
	@Override
	public Set<Ship> getShips(World world) {
		 return world.getShips();
	  }

	/**
	 * Return all asteroids located in <code>world</code>.
	 */
	@Override
	public Set<Asteroid> getAsteroids(World world) {
		  return world.getAsteroids();
	  }

	/**
	 * Return all bullets located in <code>world</code>.
	 */
	@Override
	public Set<Bullet> getBullets(World world) {
		  return world.getBullets();
	  }

	/**
	 * Add <code>ship</code> to <code>world</code>.
	 */
	@Override
	public void addShip(World world, Ship ship) {
		try {
			if(! world.overlaps(ship))
			ship.placeInWorld(world);
			else 
				ship.terminate();
			
		}
		catch(RuntimeException exc) {
			throw new ModelException("");
		}
	  }

	/**
	 * Add <code>asteroid</code> to <code>world</code>.
	 */
	@Override
	public void addAsteroid(World world, Asteroid asteroid) {
		try {
			if(! world.overlaps(asteroid))
				asteroid.placeInWorld(world);
			else
				asteroid.terminate();
		}
		catch(RuntimeException exc) {
			throw new ModelException("");
		}
	  }

	/**
	 * Remove <code>ship</code> from <code>world</code>.
	 */
	@Override
	public void removeShip(World world, Ship ship) {
		try {
		  world.removeSpaceObject(ship);
		}
		catch(RuntimeException exc) {
			throw new ModelException("");
		}
	  }

	/**
	 * Remove <code>asteroid</code> from <code>world</code>.
	 */
	@Override
	public void removeAsteroid(World world, Asteroid asteroid) {
		try {
		  world.removeSpaceObject(asteroid);
		}
		catch(RuntimeException exc) {
			throw new ModelException("");
		}
	  }

	/**
	 * Advance <code>world</code> by <code>dt<code> seconds. 
	 * 
	 * To enable explosions within the UI, notify <code>collisionListener</code>
	 * whenever an entity collides with a boundary or another entity during this
	 * method. <code>collisionListener</code> may be null. If
	 * <code>collisionListener</code> is <code>null</code>, do not call its notify
	 * methods.
	 */
	@Override
	public void evolve(World world, double dt, CollisionListener collisionListener) {
		try {
		  world.evolve(dt, collisionListener);
		}
		catch(RuntimeException exc) {
			throw new ModelException("");
		}
	  }

	/**
	 * Create a new non-null ship with the given position, velocity, radius,
	 * direction and mass.
	 * 
	 * The thruster of the new ship is initially inactive. The ship is not located
	 * in a world.
	 */
	@Override
	public Ship createShip(double x, double y, double xVelocity, double yVelocity, double radius, double direction, double mass) {
		  try {
			  return new Ship(x,y,xVelocity,yVelocity,direction,radius,300000,mass,null);
		  }
		  catch(RuntimeException exc) {
				throw new ModelException("Illegal Ship");
			}
	  }

	/**
	 * Check whether <code>o</code> is a ship.
	 * 
	 * You can use the <code>instanceof</code> operator to implement this method.
	 */
	@Override
	public boolean isShip(Object o) {
		  return (o instanceof Ship);
	  }

	/**
	 * Return the x-coordinate of <code>ship</code>.
	 */
	@Override
	public double getShipX(Ship ship) {
		  return ship.getxPosition();
	  }

	/**
	 * Return the y-coordinate of <code>ship</code>.
	 */
	@Override
	public double getShipY(Ship ship) {
		  return ship.getyPosition();
	  }

	/**
	 * Return the velocity of <code>ship</code> along the X-axis.
	 */
	@Override
	public double getShipXVelocity(Ship ship) {
		  return ship.getxVelocity();
	  }

	/**
	 * Return the velocity of <code>ship</code> along the Y-axis.
	 */
	@Override
	public double getShipYVelocity(Ship ship) {
		  return ship.getyVelocity();
	  }

	/**
	 * Return the radius of <code>ship</code>.
	 */
	@Override
	public double getShipRadius(Ship ship) {
		  return ship.getRadius();
	  }

	/**
	 * Return the direction of <code>ship</code> (in radians).
	 */
	@Override
	public double getShipDirection(Ship ship) {
		  return ship.getDirection();
	  }

	/**
	 * Return the mass of <code>ship</code>.
	 */
	@Override
	public double getShipMass(Ship ship) {
		  return ship.getMass();
	  }

	/**
	 * Return the world of <code>ship</code>.
	 */
	@Override
	public World getShipWorld(Ship ship) {
		  return ship.getWorld();
	  }

	/**
	 * Return whether <code>ship</code>'s thruster is active.
	 */
	@Override
	public boolean isShipThrusterActive(Ship ship) {
		  return ship.isActiveThruster();
	  }

	/**
	 * Enables or disables <code>ship</code>'s thruster depending on the value of
	 * the parameter <code>active</code>.
	 */
	@Override
	public void setThrusterActive(Ship ship, boolean active) {
		try {
		  ship.setActiveThruster(active);
		}
		catch(RuntimeException exc) {
			throw new ModelException("");
		}
	  }

	/**
	 * Update the direction of <code>ship</code> by adding <code>angle</code> (in
	 * radians) to its current direction. <code>angle</code> may be negative.
	 */
	@Override
	public void turn(Ship ship, double angle) {
		  if(ship.isValidDirection(angle)) 
			  ship.turn(angle);
	  }

	/**
	 * <code>ship</code> fires a bullet.
	 */
	@Override
	public void fireBullet(Ship ship) {
		try {
		  ship.shoot();
		}
		catch(RuntimeException exc) {
			throw new ModelException("");
		}
	}

	/**
	 * Create a new non-null asteroid with the given position, velocity and
	 * radius.
	 * 
	 * The asteroid is not located in a world.
	 */
	@Override
	public Asteroid createAsteroid(double x, double y, double xVelocity, double yVelocity, double radius) {
		try {  
			return new Asteroid(x, y, xVelocity, yVelocity, radius, 300000, null);
		}
		catch(RuntimeException exc) {
			throw new ModelException("");
		}
	  }

	/**
	 * Create a new non-null asteroid with the given position, velocity and
	 * radius.
	 * 
	 * The asteroid is not located in a world.
	 * 
	 * Use numbers generated by <code>random</code> to determine the direction of
	 * the children (if any) when this asteroid dies.
	 */
	@Override
	public Asteroid createAsteroid(double x, double y, double xVelocity, double yVelocity, double radius, Random random) {
		try {
		  return new Asteroid(x,y,xVelocity,yVelocity,radius,300000,null,random);
		}
		catch(RuntimeException exc) {
			throw new ModelException("");
		}
	  }

	/**
	 * Check whether <code>o</code> is an asteroid.
	 * 
	 * You can use the <code>instanceof</code> operator to implement this method.
	 */
	@Override
	public boolean isAsteroid(Object o) {
		  return (o instanceof Asteroid);
	  }

	/**
	 * Return the x-coordinate of <code>asteroid</code>.
	 */
	@Override
	public double getAsteroidX(Asteroid asteroid) {
		  return asteroid.getxPosition();
	  }

	/**
	 * Return the y-coordinate of <code>asteroid</code>.
	 */
	@Override
	public double getAsteroidY(Asteroid asteroid) {
		  return asteroid.getyPosition();
	  }

	/**
	 * Return the velocity of <code>asteroid</code> along the X-axis.
	 */
	@Override
	public double getAsteroidXVelocity(Asteroid asteroid) {
		  return asteroid.getxVelocity();
	  }

	/**
	 * Return the velocity of <code>asteroid</code> along the Y-axis.
	 */
	@Override
	public double getAsteroidYVelocity(Asteroid asteroid) {
		  return asteroid.getyVelocity();
	  }

	/**
	 * Return the radius of <code>asteroid</code>.
	 */
	@Override
	public double getAsteroidRadius(Asteroid asteroid) {
		  return asteroid.getRadius();
	  }

	/**
	 * Return the mass of <code>asteroid</code>.
	 */
	@Override
	public double getAsteroidMass(Asteroid asteroid) {
		  return asteroid.getMass();
	  }

	/**
	 * Return the world of <code>asteroid</code>.
	 */
	@Override
	public World getAsteroidWorld(Asteroid asteroid) {
		  return asteroid.getWorld();
	  }

	/**
	 * Check whether <code>o</code> is a bullet.
	 * 
	 * You can use the <code>instanceof</code> operator to implement this method.
	 */
	@Override
	public boolean isBullets(Object o) {
		  return (o instanceof Bullet);
	  }

	/**
	 * Return the x-coordinate of <code>bullet</code>.
	 */
	@Override
	public double getBulletX(Bullet bullet) {
		  return bullet.getxPosition();
	  }

	/**
	 * Return the y-coordinate of <code>bullet</code>.
	 */
	@Override
	public double getBulletY(Bullet bullet) {
		 return bullet.getyPosition();
	  }

	/**
	 * Return the velocity of <code>bullet</code> along the X-axis.
	 */
	@Override
	public double getBulletXVelocity(Bullet bullet) {
		  return bullet.getxVelocity();
	  }

	/**
	 * Return the velocity of <code>bullet</code> along the Y-axis.
	 */
	@Override
	public double getBulletYVelocity(Bullet bullet) {
		  return bullet.getyVelocity();
	  }

	/**
	 * Return the radius of <code>bullet</code>.
	 */
	@Override
	public double getBulletRadius(Bullet bullet) {
		  return bullet.getRadius();
	  }

	/**
	 * Return the mass of <code>bullet</code>.
	 */
	@Override
	public double getBulletMass(Bullet bullet) {
		  return bullet.getMass();
	  }

	/**
	 * Return the world of <code>bullet</code>.
	 */
	@Override
	public World getBulletWorld(Bullet bullet) {
		  return bullet.getWorld();
	  }

	  /**
	   * Return the source of <code>bullet</code>.
	   */
	  @Override
	  public Ship getBulletSource(Bullet bullet) {
		  return bullet.getSource();
	  }
}
